import React, {useCallback, useState, memo, useMemo} from 'react';

function Child(props: any){
  console.log('render child');
  return (
    <button onClick={props.addClick}>{props.data.number}</button>
  )
}

// 让函数组件拥有记忆功能,只有当组件的属性发生变更的时候才会刷新,否则不刷新(默认只要父组件刷新了,子组件就会刷新)
// 注意:
// 1. 只会检查prop, 但如果你的组件内部使用了 useState, useReducer or useContext, 当这些状态改变时, 依然会渲染
// React.memo only checks for prop changes. If your function component wrapped in
// If your function component wrapped in React.memo has a useState, useReducer or useContext Hook in its implementation, it will still rerender when state or context change.
//
// 2. 默认只会浅比较, 但你可以传递一个自定义比较的方法作为第二个参数
/*function MyComponent(props) {
  /!* render using props *!/
}
function areEqual(prevProps, nextProps) {
  /!*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false

  与类组件上的shouldComponentUpdate()方法不同，areEqual函数如果props相等则返回true，如果props不相等则返回false。这与shouldComponentUpdate相反
  *!/
}
export default React.memo(MyComponent, areEqual);*/

//@ts-ignore
Child = memo(Child); /**← 注意这个也是浅比较,这意味着如果你接受的属性是一个对象state,虽然它里面的值没变,但因为每次组件渲染state都是全新的(即使state是个对象(参见 00useState/03每次渲染都是独立的闭包)),而作为对象state，每次它的地址就不一样(即使其内容并没有改变),这样memo在比较时就会认为它发生了改变*/

let lastData: any;
let lastAddClick: any;
export default function App(){
  const [number, setNumber] = useState(0);
  const [name, setName] = useState('ahhh');

  // 2. 虽然使用了memo能保证组件在props不变的情况下不会被刷新
  // , 但组件每次App组件重新渲染,都会重新创建一个addClick,故每次App重新渲染Child接受到的addClick都不一样 还是会刷新
  // ,故需要是要useCallback将addClick缓存
  // useCallback是简化使用的useMemo
  const addClick = useCallback(() => setNumber(x => x + 1),[]);
  console.log('lastAddClick === addClick', lastAddClick === addClick);
  lastAddClick = addClick;

  // 3. 这里的data也和上面的2同理,也需要缓存起来
  // useMemo其实也可以缓存callback 只是比较奇怪 你选要这样写 useMemo(()=>()=>{...},[])
  // 故一般用useCallback
  // const data = {number};
  const data = useMemo(()=>({number}),[number]);
  console.log('lastData === data', lastData === data);
  lastData = data;

  return (
    <div>
      <input type="text" value={name} onChange={e => setName(e.target.value)}/>
      <hr/>
      {/*1. 改变输入框 也会刷新child 这不合理 故需要使用memo*/}
      <Child addClick={addClick} data={data}/>
    </div>
  );
}
